package complier.craft;

import java.io.CharArrayReader;
import java.util.ArrayList;
import java.util.List;

public class SimpleLexer {

    public static void main(String[] args) {
        SimpleLexer lexer = new SimpleLexer();
        List<String> testScripts = test();
        testScripts.forEach(script -> {
            System.out.println(script);
            SimpleTokenReader tokenReader = lexer.tokenize(script);
            dump(tokenReader);
            System.out.println("-----------------------");
        });

    }

    public static List<String> test() {
        List list = new ArrayList<>();
        list.add("int age = 45;");
//        list.add("inta age = 45;");
        list.add("in age = 45;");
        list.add("age >= 45;");
        list.add("age > 45;");
        list.add("age <= 45;");
        list.add("age < 45;");
        return list;
    }


    private StringBuffer tokenText = null; //临时保存token的文本
    private List<Token> tokens = null; //保存解析出来的Token
    private SimpleToken token = null; //当前正在解析的Token

    /**
     * 解析字符串，形成Token。
     * 这是一个有限状态自动机，在不同的状态中迁移。
     *
     * @param code
     * @return
     */
    public SimpleTokenReader tokenize(String code) {
        tokens = new ArrayList<Token>();
        CharArrayReader reader = new CharArrayReader(code.toCharArray());
        token = new SimpleToken();
        tokenText = new StringBuffer();
        int ich = 0;
        char ch = 0;
        DfaSate state = DfaSate.Initial;
        try {// 下一个状态 state 需要改变再改变
            while ((ich = reader.read()) != -1) {
                ch = (char) ich;
                switch (state) {
                    case Initial:
                        state = initToken(ch); //重新确定后续状态
                        break;
                    case Id:
                        if (isAlpha(ch) || isDigit((ch))) {
                            tokenText.append(ch); //保持标识符状态
                        } else {
                            state = initToken(ch); //退出标识符状态，并保存Token
                        }
                        break;
                    case GT:
                        if (ch == '=') {
                            token.type = TokenType.GE;
                            state = DfaSate.GE;
                            tokenText.append(ch);
                        } else {
                            initToken(ch);
                        }
                        break;
                    case LT:
                        if (ch == '=') {
                            token.type = TokenType.LE;
                            state = DfaSate.LE;
                            tokenText.append(ch);
                        } else {
                            initToken(ch);
                        }
                        break;
                    case LE:
                    case GE:
                    case Assignment:
                    case Plus:
                    case Minus:
                    case Star:
                    case Slash:
                    case SemiColon:
                    case LeftParen:
                    case RightParen:
                        state = initToken(ch);
                        break;
                    case IntLiteral:
                        if (isDigit(ch)) {
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int1:
                        if (ch == 'n') {
                            state = DfaSate.Id_int2;
                            tokenText.append(ch);
                        } else if (isAlpha(ch) || isDigit((ch))) {
                            state = DfaSate.Id;
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int2:
                        if (ch == 't') {
                            state = DfaSate.Id_int3;
                            tokenText.append(ch);
                        } else if (isAlpha(ch) || isDigit((ch))) {
                            state = DfaSate.Id;
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int3:
                        if (isBlank(ch)) {
                            token.type = TokenType.Int;
                            state = initToken(ch);
                        } else {
                            state = DfaSate.Id; // = 情况如何处理
                            tokenText.append(ch);
                        }
                        break;
                    default:
                }
            }
            if (tokenText.length() > 0) {
                token.text = tokenText.toString();
                tokens.add(token);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return new SimpleTokenReader(tokens);
    }

    private DfaSate initToken(char ch) {
        if (tokenText.length() > 0) {
            // 倒出已存字符，重置token
            token.text = tokenText.toString();
            tokens.add(token);

            token = new SimpleToken();
            tokenText = new StringBuffer();
        }

        DfaSate newSate = DfaSate.Initial;
        if (isAlpha(ch)) {
            if (ch == 'i') { //第一个字符是字母
                newSate = DfaSate.Id_int1;
            } else {
                newSate = DfaSate.Id;
            }
            token.type = TokenType.Identifier;
            tokenText.append(ch);
        } else if (isDigit(ch)) {  //数字
            newSate = DfaSate.IntLiteral;
            token.type = TokenType.IntLiteral;
            tokenText.append(ch);
        } else if (ch == '>') {
            newSate = DfaSate.GT;
            token.type = TokenType.GT;
            tokenText.append(ch);
        } else if (ch == '<') {
            newSate = DfaSate.LT;
            token.type = TokenType.LT;
            tokenText.append(ch);
        } else if (ch == '=') {
            newSate = DfaSate.Assignment;
            token.type = TokenType.Assignment;
            tokenText.append(ch);
        } else if (ch == ';') {
            newSate = DfaSate.SemiColon;
            token.type = TokenType.SemiColon;
            tokenText.append(ch);
        } else if (ch == '+') {
            newSate = DfaSate.Plus;
            token.type = TokenType.Plus;
            tokenText.append(ch);
        } else if (ch == '-') {
            newSate = DfaSate.Minus;
            token.type = TokenType.Minus;
            tokenText.append(ch);
        } else if (ch == '*') {
            newSate = DfaSate.Star;
            token.type = TokenType.Star;
            tokenText.append(ch);
        } else if (ch == '/') {
            newSate = DfaSate.Slash;
            token.type = TokenType.Slash;
            tokenText.append(ch);
        } else if (ch == '(') {
            newSate = DfaSate.LeftParen;
            token.type = TokenType.LeftParen;
            tokenText.append(ch);
        } else if (ch == ')') {
            newSate = DfaSate.RightParen;
            token.type = TokenType.RightParen;
            tokenText.append(ch);
        } else {
            newSate = DfaSate.Initial;
        }

        return newSate;
    }

    //是否是字母
    private boolean isAlpha(int ch) {
        return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z';
    }

    // 是否是数字
    private boolean isDigit(int ch) {
        return ch >= '0' && ch <= '9';
    }

    // 是否是空白字符
    private boolean isBlank(int ch) {
        return ch == ' ' || ch == '\n' || ch == '\t';
    }


    public static void dump(SimpleTokenReader tokenReader) {
        System.out.println("text\ttype");
        Token token = null;
        while ((token = tokenReader.read()) != null) {
            System.out.printf("%s : %s\n", token.getType(), token.getText());
        }
    }

    public void dump(TokenReader tokens) {
        System.out.println("text\ttype");
        Token token = null;
        while ((token = tokens.read()) != null) {
            System.out.printf("%s : %s\n", token.getType(), token.getText());
        }
    }

    // Token实现
    private final class SimpleToken implements Token {
        //Token 类型
        private TokenType type = null;
        // 文本值
        private String text = null;

        @Override
        public TokenType getType() {
            return type;
        }

        @Override
        public String getText() {
            return text;
        }
    }


    /**
     * 有限状态机的各种状态
     */
    private enum DfaSate {
        Initial,
        If,
        Id_if1, Id_if2,
        Else, Id_else1, Id_else2, Id_else3, Id_else4,
        Int, Id_int1, Id_int2, Id_int3,
        Id, GT, GE, LT, LE,
        Assignment,
        Plus, Minus, Star, Slash,// + - * /
        SemiColon,
        LeftParen,
        RightParen,
        IntLiteral
    }

    // SimpleTokenReader
// 简单Token 列表流
    private class SimpleTokenReader implements TokenReader {

        List<Token> tokens = null;
        int pos = 0;

        public SimpleTokenReader(List<Token> tokens) {
            this.tokens = tokens;
        }

        @Override
        public Token read() {
            if (pos < tokens.size()) {
                // 给值之后加一
                return tokens.get(pos++);
            }
            return null;
        }

        @Override
        public Token peek() {
            if (pos < tokens.size()) {
                // 原地给值
                return tokens.get(pos);
            }
            return null;
        }

        @Override
        public void unread() {
            if (pos > 0) {
                pos--;
            }
        }

        @Override
        public int getPosition() {
            return pos;
        }

        @Override
        public void setPosition(int position) {
            if (position >= 0 && position <= tokens.size()) {
                this.pos = position;
            }
        }
    }


}
